using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
namespace DAL
{
    public class FieldWithAttributesInfo<TAttribute>
    {
        public FieldInfo FieldInfo;
        public TAttribute Attribute;
    }

    public class StoredProcedureParametr:Attribute
    {
        public string ParametrName;
        public StoredProcedureParametr(string ParName, ParameterDirection direction)
        {
            ParametrName = ParName;
            Direction = direction;
         
        }
        public StoredProcedureParametr(ParameterDirection direction)
        {
            ParametrName = null;
            Direction = direction;

        }

        public ParameterDirection Direction;

    }

    public class StoredProcedureReturnParametr : StoredProcedureParametr
    {
        public StoredProcedureReturnParametr(string ParName, ParameterDirection direction):base(ParName, direction)
        {
         
        }
        public StoredProcedureReturnParametr(ParameterDirection direction):base(direction)
        {
            

        }
    }

    public class InParameter : StoredProcedureParametr
    {
        public InParameter():base(ParameterDirection.Input)
        {
        }
        public InParameter(string ParName)
            : base(ParName, ParameterDirection.Input)
        {
        }
    }


    public class OutParameter : StoredProcedureReturnParametr
    {
       
        public OutParameter()
            : base(ParameterDirection.Output)
        {
        }
        public OutParameter(string ParName)
            : base(ParName, ParameterDirection.Output)
        {
        }
    }

    public class ReturnParameter : StoredProcedureReturnParametr
    {
        public ReturnParameter()
            : base(ParameterDirection.ReturnValue)
        {
        }
        public ReturnParameter(string ParName)
            : base(ParName, ParameterDirection.ReturnValue)
        {
        }
    }
    public class MapItem
    {
        public MapItem()
        {
        }
        public MapItem(string from, string to, Type type ,int index)
        {
            From = from;
            To = to;
            
            Index = index;
            EntityType = type;

        }
        public int Index;
        public string From;


        public string To;


        private Type _EntityType;
        public Type EntityType 
        {
            get
            {
                return _EntityType;
            }
            set
            {
                _EntityType = value;

                Type type = value;
                
                MemberInfo[] tmp;
                string []  Names  = {To , "_"+To , "<"+To+">k__BackingField"};
                MemberInfo meber=null;
                do
                {


                    for (int i =0; i< Names.Length; i++)
                    {
                        tmp = type.GetMember(Names[i], BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase );
                        for (int j = 0; j < tmp.Length; j++ )
                        {
                            if (tmp[j].MemberType == MemberTypes.Property)
                            {
                                
                                if ( ((PropertyInfo)tmp[j]).CanWrite)
                                {
                                    Property = (PropertyInfo) (meber = tmp[j]);
                                    return;
                                }
                            }

                            if (tmp[j].MemberType == MemberTypes.Field)
                            {

                                Field =(FieldInfo) (meber = tmp[j]);
                                return;
                            }
                        }
                    }



                } while (meber == null && (type = type.BaseType) != null);
              
                
            }
        }
        
        public PropertyInfo Property;
        public FieldInfo Field;

     /*   public PropertyInfo Property;
        public FieldInfo Field;
        */

     /*   public void SetValue(object obj, object value)
        {
            if (Property == null && Field == null)
            {
                return;
             //   throw new DbException(DbExceptionType.LoadData, new Exception("can`t find property " + To));
            }
            if (obj == null)
                throw new DbException(DbExceptionType.LoadData, new Exception("null object reference"));

            try
            {
                if (Property != null)
                {
                    Property.SetValue(obj, value, null);
                }
                else
                {
                    Field.SetValue(obj,value );
                }
                
            }
            catch (Exception exp)
            {
                throw new DbException(DbExceptionType.LoadData, new Exception( "can`t set value for property "+ Property.Name + " ->" + exp.ToString() ));
            }
        }*/
    }
    public class Map:Attribute 
    {
        private bool _MapAll = false;
        public bool MapAll
        {
            get
            {
                return _MapAll;
            }
        }

        protected void ConstructMap(string[] maps)
        {
            string[] tmp;
            if (maps != null)
            {
                foreach (string m in maps)
                {

                    tmp = m.Split("=".ToCharArray());
                    if (tmp != null)
                    {
                        MapItem mItem = new MapItem();
                        mItem.To = tmp[0];

                        if (tmp.Length > 1)
                        {
                            mItem.From = tmp[1];
                        }
                        else
                        {
                            mItem.From = tmp[0];
                        }

                        _Items.Add(mItem);
                    }


                }
            }
        }


        public Map(params string[] maps)
        {
            if (maps==null || string.IsNullOrEmpty(maps[0]))
            {
                return;
            }
            if (maps[0] == "*")
            {
                _MapAll = true;
                return;
            }

            ConstructMap(maps);
        }
        public Map(string mapString)
        {
            if (string.IsNullOrEmpty(mapString))
            {
                return;
            }
           
            if (mapString == "*")
            {
                _MapAll = true;
                return;
            }

            string[] maps = mapString.Split(",".ToCharArray());

            ConstructMap(maps);
        }
        private List<MapItem> _Items = new List<MapItem>();

        public List<MapItem> Items
        {
            get
            {
                return _Items;
            }
        }
        public void FromEntityToSP(object Entity,StoredProcedure sp)
        {
            if (Entity == null || sp == null)
                return ;

            

            Type spt = sp.GetType();
            Type et = Entity.GetType();

            if (MapAll)
            {
                foreach (FieldWithAttributesInfo<InParameter> l in Reflector.GetFieldsByAttribute<InParameter>(sp.GetType(), typeof(InParameter)))
                {
                    FieldInfo fi = l.FieldInfo;
                    if (typeof(ValueType).IsInstanceOfType(Entity) || typeof(string).IsInstanceOfType(Entity)
)                   {
                        fi.SetValue(sp, Entity);
                        return;
                    }


                    PropertyInfo pi =  et.GetProperty(l.FieldInfo.Name);
                    

                    if (pi!=null)
                        fi.SetValue(sp, pi.GetValue(Entity, null));
                }
                
                
            }
            else
            {
                foreach (MapItem m in sp.MapParams.Items)
                {
                    FieldInfo fi =  spt.GetField(m.From);

                    if (typeof(ValueType).IsInstanceOfType(Entity) || typeof(string).IsInstanceOfType(Entity) )
                    {
                        fi.SetValue(sp, Entity);
                        return;
                    }


                    PropertyInfo pi = et.GetProperty(m.To);
                    if (fi != null && pi != null)
                    {
                        fi.SetValue(sp, pi.GetValue(Entity,null));
                    }

                }
            }
        }
        public void ItemsFromReaderToEntity(System.Data.Common.DbDataReader dr, Type type)
        {
            
                if (MapAll)
                {


                    _Items.Clear();

                    for (int i = 0; i < dr.FieldCount; i++)
                    {
                        string ColumnName = "unknown";
                        try
                        {
                            ColumnName = dr.GetName(i);

                            _Items.Add(new MapItem(ColumnName, ColumnName, type, i));
                        }
                        catch(Exception exp)
                        {
                            throw new DbException(DbExceptionType.MapParams, new Exception("can`t map param " + ColumnName + " to " + ColumnName, exp)  );
                        }
                    }
                }
                else
                {

                    foreach (MapItem m in _Items)
                    {
                        try
                        {
                            m.Index = dr.GetOrdinal(m.From);
                            m.EntityType = type;

                        }
                        catch (Exception exp)
                        {
                            throw new DbException(DbExceptionType.MapParams, new Exception("can`t map param " + m.From + " to " + m.To , exp));
                        }
                    }


                }

            
        }



        public void FromSPToEntity( object  Entity, StoredProcedure sp)
        {
            
            if (Entity == null || sp == null)
                return;



            Type spt = sp.GetType();
            Type et = Entity.GetType();

            if (MapAll)
            {
                foreach (FieldWithAttributesInfo<OutParameter> l in Reflector.GetFieldsByAttribute<OutParameter>(sp.GetType(), typeof(OutParameter)))
                {
                    FieldInfo fi = l.FieldInfo;
                    if (typeof(ValueType).IsInstanceOfType(Entity) || typeof(string).IsInstanceOfType(Entity))
                    {
                        
                       // Entity =  fi.GetValue(sp);
                        return;
                    }


                    PropertyInfo pi = et.GetProperty(l.FieldInfo.Name);


                    if (fi != null && pi != null)
                        pi.SetValue(Entity, fi.GetValue(sp) , null);
                }


            }
            else
            {
                foreach (MapItem m in sp.MapParams.Items)
                {
                    FieldInfo fi = spt.GetField(m.From);

                    if (typeof(ValueType).IsInstanceOfType(Entity) || typeof(string).IsInstanceOfType(Entity))
                    {
                       // Entity = fi.GetValue(sp);
                        return;
                    }


                    PropertyInfo pi = et.GetProperty(m.To);

                    if (fi != null && pi != null)
                        pi.SetValue(Entity, fi.GetValue(sp),null);


                    

                }
            }

            
        }
    }
    public class SP : Attribute
    {
        public string Name;
        public SP(string name)
        {
            Name = name;
        }
    }
    public class spTimeOut : Attribute
    {
        public int Value;
        public spTimeOut(int value)
        {
            Value = value;
        }
    }
    public class SQL : Attribute
    {
        public string Text;
        public SQL(string text)
        {
            Text = text;
        }

        public static implicit operator SQL(string s)
        {
           return new SQL (s);
        }

  
    }
       
    public class TableName : Attribute
    {
        public string Name;
        public TableName(string name)
        {
            Name = name;
        }
    }

    public class ConnectionName : Attribute
    {
        public string Name;
        public ConnectionName(string name)
        {
            Name = name;
        }
    }
    public class ConnectionString : Attribute
    {
        public string String;
        public ConnectionString(string s)
        {
            String = s;
        }
    }

    public class Provider : Attribute
    {
        public string Name;
        public Provider(string name)
        {
            Name = name;
        }
    }

    public class spType : Attribute
    {
       
        public Type Type;
        public spType(string type)
        {
            this.Type = System.Type.GetType(type);
        }
        
        public spType(Type type)
        {
            this.Type = type;
        }
    }

    


    public class spGetOne : spType
    {
        public spGetOne(string type) : base(type) {}
        public spGetOne(Type type) : base(type) { }
    }
    public class spGetAll : spType
    {
        public spGetAll(string type) : base(type) {}
        public spGetAll(Type type) : base(type) {}
    }

    public class spGetMany : spType
    {
        public spGetMany(string type) : base(type) { }
        public spGetMany(Type type) : base(type) { }
    }
    
    public class spAdd : spType
    {
        public spAdd(string type) : base(type) {}
        public spAdd(Type type) : base(type) {}
    }
    public class spSave : spType
    {
        public spSave(string type) : base(type) {}
        public spSave(Type type) : base(type) {}
    }
    public class spRemove : spType
    {
        public spRemove(string type) : base(type) { }
        public spRemove(Type type) : base(type) { }
    }


    public class spName : Attribute
    {
        public string Text;
        
        public spName(string text)
        {
            Text = text;
        }

       
    }

    public class spNamePrefix : spName
    {
        public spNamePrefix(string Text) : base(Text) { }
    }
    public class spNameSeparator : spName
    {
        public spNameSeparator(string Text) : base(Text) { }
    }
    public class spNameGetAll : spName
    {
        public spNameGetAll(string Text) : base(Text) { }
    }
    public class spNameGetMany : spName
    {
        public spNameGetMany(string Text) : base(Text) { }
    }
    public class spNameGetOne : spName
    {
        public spNameGetOne(string Text) : base(Text) { }
    }
    public class spNameAdd : spName
    {
        public spNameAdd(string Text) : base(Text) { }
    }
    public class spNameSave : spName
    {
        public spNameSave(string Text) : base(Text) { }
    }
    public class spNameRemove : spName
    {
        public spNameRemove(string Text) : base(Text) { }
    }
}
